#include "MilovoTables.h"

int CheckDebugLevel(string);
void InitAgentsNetwork(string , MilinitParams &, SystemParams &, Agents *, Recipes *, Artifacts *, MilovoRandom &, MilovoEnvironment &);
bool LoadEventsTable(string, Events &);

void SaveDataInTextDB(SimulationParams &, Agents *, Recipes *, Artifacts *, Requests *, ScaffoldingStructure *, MilovoEnvironment &);
void LoadDataInTextDB(SimulationParams &, Agents *, Recipes *, Artifacts *, Mil_Int, Requests *, MilovoEnvironment &);

void ActionAgent(Mil_Int , Agents *, Recipes *, Artifacts *, ScaffoldingStructure *, Requests *, MilinitParams &, SystemParams &, 
					  SimulationParams &, Events &, MilovoRandom &, MilovoEnvironment &);

void UpdateSystem(MilinitParams &, Agents *, Recipes *, Artifacts *, ScaffoldingStructure *, Requests *, MilovoEnvironment &);
bool CreateNewAgent(Agents *tmpTabAgents, Recipes *tmpTabRecipes, Artifacts *tmpTabArtifacts, Requests *, 
						  MilovoRandom &MiloRand, MilinitParams &tmpInitParam, SystemParams &tmpMilovoParam, MilovoEnvironment &tmpMiloEnv);

void ExtractNetworkForPajek(string, Agents *, Recipes *, Artifacts *, Mil_Int);

int main (int argc, char * const argv[])
{
	MilovoEnvironment MiloEnv;	
	if(argc > 2)
		MiloEnv.SetDebugLevel(CheckDebugLevel(string(argv[2])));
	
	Agents *TabAgents = new Agents;
	Recipes *TabRecipes = new Recipes;
	Artifacts *TabArtifacts = new Artifacts;
	Requests *TabRequests = new Requests;
	MilovoRandom MiloRand;
	
	SimulationParams SimulaParam;
	SimulaParam.LoadSimulationParam(argv[1]);

	MilinitParams InitParam;
	InitParam.LoadInitNetParam(SimulaParam.GetPathMilinitConf());
	InitParam.LoadRawMaterials(SimulaParam.GetWorkingDir());
	InitParam.LoadOperators(SimulaParam.GetWorkingDir());
	
	SystemParams MilovoParam;
	MilovoParam.LoadSystemParam(SimulaParam.GetPathMilovoParamConf());

	Events TabEvents;
	LoadEventsTable(SimulaParam.GetPathEventsTable(), TabEvents);
	
	ScaffoldingStructure *TabScaffoldigStructure = new ScaffoldingStructure();
	
	if(SimulaParam.GetNewSimulation() == 1)
	{
		cout << "*** Init agents network [begin] ***" << endl;
		// Init the initial agents network
		InitAgentsNetwork(SimulaParam.GetWorkingDir(), InitParam, MilovoParam, TabAgents, TabRecipes, TabArtifacts, MiloRand, MiloEnv);
		if(MilovoParam.GetScaffoldingMaxLevel() > 0)
			InitScaffoldingStructure(TabAgents, TabScaffoldigStructure, MilovoParam, MiloEnv.GetDebugLevel());

		SaveDataInTextDB(SimulaParam, TabAgents, TabRecipes, TabArtifacts, TabRequests, TabScaffoldigStructure, MiloEnv);
		MiloEnv.SetCurrentStep(SimulaParam.GetNumFirstStep());
		cout << "*** Init agents network [end] ***" << endl;
	}
	else
	{
		cout << "*** Load agents network [begin] ***" << endl;
		LoadDataInTextDB(SimulaParam, TabAgents, TabRecipes, TabArtifacts, MiloEnv.GetCurrentStep(), TabRequests, MiloEnv);
		MiloEnv.SetCurrentStep(SimulaParam.GetNumFirstStep() + 1);
		cout << "*** Load agents network [end] ***" << endl;
	}
	
	MiloRand.seed(MilovoParam.GetSeedRandomValue());
	if(MiloEnv.GetDebugLevel() >= 1)
		cout << "Start Simulation - Init seed: " << InitParam.GetSeedRandomValue() << " MiloRand seed: " << MilovoParam.GetSeedRandomValue() << endl;

	for(MiloEnv.GetCurrentStep(); MiloEnv.GetCurrentStep() <= SimulaParam.GetNumLastStep(); MiloEnv.SetCurrentStep(MiloEnv.GetCurrentStep() + 1))
	{		
		vector<Mil_Int> VectAgentsAlive;
		GetAgentsAlive(VectAgentsAlive, TabAgents, MiloEnv.GetDebugLevel());
		// mix the order of agents in a support vector
		random_shuffle(VectAgentsAlive.begin(), VectAgentsAlive.end());
		vector<Mil_Int>::iterator VectAgentsAliveIter = VectAgentsAlive.begin();
		Mil_Int NumSelection = 0;
		while(NumSelection < VectAgentsAlive.size())
		{
			Agents::iterator AgentSelectedIter;
			// if SelectionMode = 1 the selection of an agent is 
			// random, else the selection keep all agents, one per turn
			if(MilovoParam.GetAgentSelectionMode() == 1)
				AgentSelectedIter = TabAgents->find(VectAgentsAlive[MiloRand.GetIntRandom(0, (VectAgentsAlive.size() - 1))]);
			else AgentSelectedIter = TabAgents->find(*VectAgentsAliveIter);
			if(AgentSelectedIter != TabAgents->end())
			{
				NumSelection++;
				ActionAgent(AgentSelectedIter->second.GetID(), TabAgents, TabRecipes, TabArtifacts, TabScaffoldigStructure, 
								TabRequests, InitParam, MilovoParam, SimulaParam, TabEvents, MiloRand, MiloEnv);
			}
			VectAgentsAliveIter++;
		}
		if(MiloEnv.GetCurrentStep() % MilovoParam.GetSaveDatabaseFreq() == 0)
		{
			cout << "Step: " << MiloEnv.GetCurrentStep() << endl;
//			ExtractNetworkForPajek(SimulaParam.GetWorkingDir(), TabAgents, TabRecipes, TabArtifacts, MiloEnv.GetCurrentStep());
			SaveDataInTextDB(SimulaParam, TabAgents, TabRecipes, TabArtifacts, TabRequests, TabScaffoldigStructure, MiloEnv);
		}
		// born new agent
		if(MilovoParam.GetGrowCoefficient() > 0)
		{
			Mil_Int NumArtifactsThisStep = TabArtifacts->size();
			MilovoParam.SetBornNewAgents(MilovoParam.GetGrowCoefficient() * NumArtifactsThisStep);
			Mil_Double HowManyNewAgent = floor(MilovoParam.GetBornNewAgents());
			for(Mil_Double i = 0; i < HowManyNewAgent; i++)
				CreateNewAgent(TabAgents, TabRecipes, TabArtifacts, TabRequests, MiloRand, InitParam, MilovoParam, MiloEnv);

			if(MiloRand.GetDoubleRandom(0.0, 1.0) <= (MilovoParam.GetBornNewAgents() - HowManyNewAgent))
				CreateNewAgent(TabAgents, TabRecipes, TabArtifacts, TabRequests, MiloRand, InitParam, MilovoParam, MiloEnv);
		}
		else
		{
			if(MiloRand.GetDoubleRandom(0.0, 1.0) <= MilovoParam.GetBornNewAgents())
				CreateNewAgent(TabAgents, TabRecipes, TabArtifacts, TabRequests, MiloRand, InitParam, MilovoParam, MiloEnv);
		}
		// update the GrowProbability value
		MiloEnv.SetGrowProbability(MilovoParam.GetBornNewAgents());
		// update system
		UpdateSystem(InitParam, TabAgents, TabRecipes, TabArtifacts, TabScaffoldigStructure, TabRequests, MiloEnv);
	}
	// clear milovo environment
	TabAgents->clear();
	delete TabAgents;
	TabRecipes->clear();
	delete TabRecipes;
	TabArtifacts->clear();
	delete TabArtifacts;
	TabRequests->clear();
	delete TabRequests;

//	if(MiloEnv.GetDebugLevel() >= 1)
		cout << "End Simulation - Init seed: " << InitParam.GetSeedRandomValue() << " MiloRand seed: " << MilovoParam.GetSeedRandomValue() << endl;
	
	return 0;
}

int CheckDebugLevel(string strDebugLevel)
{
	int tmpDebugLevel = 0;
	int tmpEqualPos = strDebugLevel.find("=");
	string tmpValueDebugLevel(strDebugLevel, tmpEqualPos + 1, strDebugLevel.size());
	tmpDebugLevel = atoi(tmpValueDebugLevel.c_str());
	
	return(tmpDebugLevel);
}
